技巧47 直接行动:在容器上执行命令

在Docker早期,许多用户会在他们的镜像里添加SSH服务,这样一来便可以从外部通过shell来访问它们。Docker不主张这样做,它认为这相当于把容器当成一台虚拟机(而我们知道,容器并不是虚拟机),并且这给本不应该需要它的系统带来了额外的进程开销。很多人对此持反对意见的原因在于,一旦容器启动了,没有一个简便的办法进到容器里面。结果便是,Docker引入了 exec 命令,它是一个更优雅地解决干涉和检索启动后的容器内部问题的解决方案。我们这里也将讨论此命令。

问题

想要在一个正在运行的容器里执行一些命令。

解决方案

使用 dockerexec 命令。

下列命令会在后台(带上 -d 标志)启动一个容器,然后告诉它一直休眠(不做任何事情)。我们把这条命令命名为 sleeper

docker run -d --name sleeper debian sleep infinity

现在已经启动了一个容器,可以用Docker的 exec 命令对它执行一些操作。该命令可以看成有3种基本模式,如表6-1所示。

表6-1 Docker exec 模式
模 式 描 述
基本 在命令行上对容器同步地执行命令
守护进程 在容器的后台执行命令
交互 执行命令并允许用户与其交互

我们先介绍一下基本模式。下列命令在 sleeper 容器内部执行了一个 echo 命令。

$ docker exec sleeper echo "hello host from container"
hello host from container

注意,该命令的结构和 dockerrun 命令非常相似,但是把镜像ID替换成一个正在运行的容器的ID。 echo 命令指代的是容器里面的echo二进制文件,而非容器外部的。

守护进程模式会在后台执行命令,用户无法在终端看到输出结果。这可能适用于一些常规的清理任务,在这些任务中,你希望敲完即走,如清理日志文件。

$ docker exec -d sleeper \  ⇽--- 执行命令时加上-d标志即可在后台以守护进程的形式运行,类似dcoker run
 find / -ctime 7 -name '*log' -exec rm {} \;  ⇽--- 删除所有在最近7天没有做过更改并且以log结尾的文件
 $  ⇽--- 无论需要多长时间完成这一操作,该命令都会立即返回

最后,我们来试试交互模式。这种模式允许用户在容器里执行任何想要执行的命令。要启用这一功能,通常需要指定用来在运行时交互的shell,在如下代码里便是bash:

$ docker exec -i -t sleeper /bin/bash
root@d46dc042480f:/#

-i-t 参数同我们所熟悉的 dockerrun 做着相同的事情——它们会让命令成为可交互的,然后设置一个TTY设备,以便shell可以正常工作。在执行该命令后,用户便拿到了一个在容器里运行的命令提示符。

讨论

当出现问题或者想要弄清楚容器在做什么时,跳到容器里是必不可少的调试步骤。往往不太可能使用技巧44里提到的绑定和解绑方法,因为容器内的进程通常运行在前台,无法访问shell提示符。由于 exec 允许用户指定想要运行的二进制文件,这便不再是问题……只要容器文件系统上实际存在那份想要运行的二进制文件即可。

特别的是,如果你使用技巧58创建一个带有单个二进制文件的容器,那么将无法启动shell。在这种情况下,可能想坚持采用技巧57作为允许 exec 执行的低开销办法。

results matching ""

    No results matching ""